// 包声明：定义当前文件属于main包，表示这是一个可执行程序的入口包
package main

// 导入所需的包
import (
	"context"
	"encoding/json"
	"flag"
	"fmt"      // 格式化输入输出包
	"net/http" // HTTP服务器和客户端包
	"os"       // 操作系统接口包
	"path"     // 文件路径操作包
	"sen-golang-study/go_game_study/hero_story_go_server/biz_server/handler"
	"sen-golang-study/go_game_study/hero_story_go_server/common"
	"sen-golang-study/go_game_study/hero_story_go_server/common/log"
	"strings"
	"time"

	"github.com/gorilla/websocket" // Gorilla WebSocket库，用于WebSocket通信
	clientv3 "go.etcd.io/etcd/client/v3"
)

// 命令行参数
var (
	listenAddr       = flag.String("listen", "127.0.0.1:123456", "HTTP服务器监听地址")
	etcdEndpoints    = flag.String("etcd", "127.0.0.1:2379", "etcd端点地址，多个地址用逗号分隔")
	serverId         = flag.Uint("server-id", 1001, "服务器ID")
	responsibilities = flag.String("responsibilities", "LOGIN,GAME", "服务器职责，用逗号分隔")
	maxConnections   = flag.Uint("max-connections", 1000, "最大连接数")
)

type GameServerInfo struct {
	ServerId    uint32 `json:"serverId"`
	ServiceAddr string `json:"serverAddr"`
}

// 定义WebSocket升级器，用于将HTTP连接升级为WebSocket连接
var upgrader = &websocket.Upgrader{
	ReadBufferSize:  1024, // 读缓冲区大小，单位字节
	WriteBufferSize: 1024, // 写缓冲区大小，单位字节
	// CheckOrigin函数用于检查WebSocket连接的来源是否被允许
	// 这里返回true表示允许所有来源的连接（生产环境中应该更严格）
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

var sessionId uint32 = 0

// 游戏前端地址：http://cdn0001.afrxvk.cn/hero_story/demo/step040/index.html?serverAddr=127.0.0.1:12345
// 应用程序主函数
// 这是程序的入口点，当程序启动时会首先执行这个函数
func main() {
	// 解析命令行参数
	flag.Parse()

	// 打印服务器启动信息
	fmt.Printf("启动游戏服务，监听地址: %s，etcd端点: %s\n", *listenAddr, *etcdEndpoints)

	// 获取当前可执行文件的路径
	ex, err := os.Executable()

	// 检查获取可执行文件路径是否出错
	if nil != err {
		// 如果出错，使用panic终止程序并输出错误信息
		panic(err)
	}

	// 配置日志文件路径
	// path.Dir(ex)获取可执行文件所在目录，然后拼接日志文件路径
	log.Config(path.Dir(ex) + "/log/biz_server.log")

	registerServer()

	// 注册HTTP路由处理函数
	// 当客户端访问"/websocket"路径时，会调用webSocketHandshake函数
	http.HandleFunc("/websocket", webSocketHandshake)

	// 启动HTTP服务器，监听127.0.0.1:12345地址
	// 第二个参数为nil表示使用默认的HTTP多路复用器
	_ = http.ListenAndServe(*listenAddr, nil)
}

// WebSocket握手函数
// 处理WebSocket升级协议，将HTTP连接升级为WebSocket连接
// w: HTTP响应写入器，用于向客户端发送响应
// r: HTTP请求对象，包含客户端发送的请求信息
func webSocketHandshake(w http.ResponseWriter, r *http.Request) {
	if nil == w ||
		nil == r {
		return
	}

	conn, err := upgrader.Upgrade(w, r, nil)

	if nil != err {
		log.Error("WebSocket upgrade error, %v+", err)
		return
	}

	defer func() {
		_ = conn.Close()
	}()

	log.Info("有新客户端连入")

	sessionId++

	gatewayConn := handler.ClientMsgHandler{
		ClientId:   0,
		ClientConn: conn,
		MsgChan:    nil,
	}

	gatewayConn.InitAndStart()
}

func registerServer() {
	// 解析etcd端点地址
	endpoints := strings.Split(*etcdEndpoints, ",")
	for i, endpoint := range endpoints {
		endpoints[i] = strings.TrimSpace(endpoint)
	}

	etcdClient, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		log.Error("Etcd客户端创建失败, %v", err)
		return
	}

	go loopGrantAndPut(etcdClient)
}

func loopGrantAndPut(etcdClient *clientv3.Client) {
	defer func() {
		_ = etcdClient.Close()
	}()

	// 解析职责字符串
	respList := strings.Split(*responsibilities, ",")
	var serverResponsibilities []common.ServerResponsibility
	for _, resp := range respList {
		resp = strings.TrimSpace(resp)
		if resp != "" {
			serverResponsibilities = append(serverResponsibilities, common.ServerResponsibility(resp))
		}
	}

	serverInfo := &common.GameServerInfo{
		ServerId:         uint32(*serverId),
		ServiceAddr:      *listenAddr,
		Responsibilities: serverResponsibilities,
		MaxConnections:   uint32(*maxConnections),
		CurrentLoad:      0, // 初始负载为0
	}

	marshalBytes, err := json.Marshal(serverInfo)
	if err != nil {
		log.Error("GameServerInfo序列化为JSON失败, %v", err)
		return
	}

	for {
		// 更新当前负载（这里可以根据实际情况计算）
		serverInfo.CurrentLoad = getCurrentLoad()
		marshalBytes, _ = json.Marshal(serverInfo)

		grantResp, _ := etcdClient.Grant(context.TODO(), 10)
		key := fmt.Sprintf("hero_story.server/game_server_%d", *serverId)
		_, _ = etcdClient.Put(context.TODO(), key, string(marshalBytes), clientv3.WithLease(grantResp.ID))
		time.Sleep(5 * time.Second)
	}
}

// getCurrentLoad 获取当前负载（统计活跃连接数）
func getCurrentLoad() uint32 {
	// 获取命令上下文组实例并统计活跃连接数
	ctxGroup := handler.GetCmdContextGroup()
	if ctxGroup == nil {
		return 0
	}

	return ctxGroup.GetActiveConnectionCount()
}
